﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Acb.Core.Data;
using Acb.Core.Extensions;
using Acb.Core.Helper;
using Acb.Dapper;
using Acb.Dapper.Domain;
using Dapper;
using OAuth.Server.Domain.Entities;
using OAuth.Server.Domain.Enums;

namespace OAuth.Server.Domain
{
    public class ClientRepository : DapperRepository<TClient>
    {
        public async Task<IEnumerable<TSecret>> QuerySecrets(Guid clientId)
        {
            var type = typeof(TSecret);
            var sql =
                $"SELECT {type.Columns()} FROM [{type.PropName()}] WHERE [{nameof(TSecret.Source)}]=@source AND [{nameof(TSecret.SourceId)}]=@sourceId";
            sql = Connection.FormatSql(sql);
            return await Connection.QueryAsync<TSecret>(sql, new { source = SecretSource.Client, sourceId = clientId });
        }

        public async Task<TClientConfig> QueryConfig(Guid clientId)
        {
            return await Connection.QueryByIdAsync<TClientConfig>(clientId);
        }

        public async Task<IDictionary<string, string>> QueryProperties(Guid clientId)
        {
            var type = typeof(TClientProperty);
            var sql =
                $"SELECT {type.Columns()} FROM [{type.PropName()}] WHERE [{nameof(TClientProperty.ClientId)}]=@clientId";
            sql = Connection.FormatSql(sql);
            var models = await Connection.QueryAsync<TClientProperty>(sql, new { clientId });
            return models.ToDictionary(k => k.Key, v => v.Value);
        }

        public async Task<List<Claim>> QueryClaims(Guid clientId)
        {
            var type = typeof(TClientClaim);
            var sql =
                $"SELECT {type.Columns()} FROM [{type.PropName()}] WHERE [{nameof(TClientClaim.ClientId)}]=@clientId";
            sql = Connection.FormatSql(sql);
            var models = await Connection.QueryAsync<TClientClaim>(sql, new { clientId });
            return models.Select(t => new Claim(t.Type, t.Value)).ToList();
        }

        public async Task<int> InsertSecret(TSecret secret)
        {
            secret.Id = IdentityHelper.NewSequentialGuid();
            secret.Source = (byte)SecretSource.Client;
            return await Connection.InsertAsync(secret);
        }

        public async Task<int> InsertProperty(TClientProperty property)
        {
            property.Id = IdentityHelper.NewSequentialGuid();
            return await Connection.InsertAsync(property);
        }

        public async Task<int> InsertClaim(TClientClaim claim)
        {
            claim.Id = IdentityHelper.NewSequentialGuid();
            return await Connection.InsertAsync(claim);
        }

        public async Task<int> InsertConfig(TClientConfig config)
        {
            var type = typeof(TClientConfig);
            var sql =
                $"insert into [{type.PropName()}] ([Id],[Scopes],[Origins],[Grants],[Providers],[Uris],[LogoutUris]) values (@Id,@Scopes::json,@Origins::json,@Grants::json,@Providers::json,@Uris::json,@LogoutUris::json)";
            sql = Connection.FormatSql(sql);
            return await Connection.ExecuteAsync(sql, config);
        }

        public async Task<int> InsertSecrets(ICollection<TSecret> secrets)
        {
            secrets.Foreach(t =>
            {
                t.Id = IdentityHelper.NewSequentialGuid();
                t.Source = (byte)SecretSource.Client;
            });
            return await Connection.InsertAsync<TSecret>(secrets);
        }

        public async Task<int> InsertProperties(ICollection<TClientProperty> properties)
        {
            properties.Foreach(t => t.Id = IdentityHelper.NewSequentialGuid());
            return await Connection.InsertAsync<TClientProperty>(properties);
        }

        public async Task<int> InsertClaims(ICollection<TClientClaim> claims)
        {
            claims.Foreach(t => t.Id = IdentityHelper.NewSequentialGuid());
            return await Connection.InsertAsync<TClientClaim>(claims);
        }
    }
}
